// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <assert.h>
#include <string.h>

#include "pty-fifo.h"

static_assert((PTY_FIFO_SIZE & (PTY_FIFO_SIZE - 1)) == 0, "fifo size not power of two");

#define PTY_FIFO_MASK (PTY_FIFO_SIZE - 1)

size_t pty_fifo_write(pty_fifo_t* fifo, const void* data, size_t len, bool atomic) {
  size_t avail = PTY_FIFO_SIZE - (fifo->head - fifo->tail);
  if (avail < len) {
    if (atomic) {
      return 0;
    }
    len = avail;
  }

  size_t offset = fifo->head & PTY_FIFO_MASK;

  avail = PTY_FIFO_SIZE - offset;
  if (len <= avail) {
    memcpy(fifo->data + offset, data, len);
  } else {
    memcpy(fifo->data + offset, data, avail);
    auto p = static_cast<const uint8_t*>(data);
    memcpy(fifo->data, p + avail, len - avail);
  }

  fifo->head += static_cast<uint32_t>(len);
  return len;
}

size_t pty_fifo_read(pty_fifo_t* fifo, void* data, size_t len) {
  size_t avail = fifo->head - fifo->tail;
  if (avail < len) {
    len = avail;
  }

  size_t offset = fifo->tail & PTY_FIFO_MASK;

  avail = PTY_FIFO_SIZE - offset;
  if (len <= avail) {
    memcpy(data, fifo->data + offset, len);
  } else {
    memcpy(data, fifo->data + offset, avail);
    auto p = static_cast<uint8_t*>(data);
    memcpy(p + avail, fifo->data, len - avail);
  }

  fifo->tail += static_cast<uint32_t>(len);
  return len;
}
